/*
 * sdc.c
 *
 * Created: 2011.10.26 10:03:58
 *  Author: Administrator
 */ 

#include "sdc.h"

void SPI_init_lospeed()
{
	SPCR=(1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0);
}

void SPI_init_hispeed()
{
	SPCR=(1<<SPE)|(1<<MSTR);
}

void SPI_write(unsigned char buff)
{
	SPDR=buff;
	while(!(SPSR&(1<<SPIF)));
}

unsigned char SPI_read()
{
	SPDR=0xff;
	while(!(SPSR&(1<<SPIF)));
	return SPDR;
}

void SDC_enable()
{
	SDC_PORT&=~(1<<SDC_CS);
}

void SDC_disable()
{
	SDC_PORT|= 1<<SDC_CS;
}

void SDC_sendcommand_u8(unsigned char cmd,unsigned char b0,unsigned char b1,unsigned char b2,unsigned char b3,unsigned char crc)
{
	SPI_write(cmd|0x40);
	SPI_write(b0);
	SPI_write(b1);
	SPI_write(b2);
	SPI_write(b3);
	SPI_write(crc);
	SPI_write(0xff);
}

void SDC_sendcommand(unsigned char cmd,unsigned long arg,unsigned char crc)
{
	SDC_sendcommand_u8(cmd,(unsigned char)(arg>>24),(unsigned char)(arg>>16),(unsigned char)(arg>>8),arg,crc);
}

unsigned char CMD0()
{
	unsigned char r1;
	unsigned char j;
	
	for (j=0;j<10;j++)
	{
		SPI_write(0xff);
	}
		
	SDC_sendcommand(GO_IDLE_STATE,0,0x95);
		
	r1=SPI_read();
		
	if (r1==1)
	{
		return SDC_IN_IDLE_STATE;
	}
	
	return SDC_ERROR;
}

unsigned long CMD8()
{
	unsigned char r1;
	unsigned long r7=0;
	
	unsigned char j;
	
	for (j=0;j<10;j++)
	{
		SPI_write(0xff);
	}
	
	SDC_sendcommand(SEND_IF_COND,0x1aa,0x87);
	r1=SPI_read();
	if (r1==1)
	{
		for (j=0;j<4;j++)
		{
			r7<<=8;
			r7|=SPI_read();
		}
		return r7;
	}	
	else if (r1==5)
	{
		return SDC_V1;
	}
	return SDC_ERROR;
}

void CMD16()
{
	SDC_sendcommand(SET_BLOCKLEN,512,0);
}

unsigned long CMD58()
{
	unsigned char r1;
	unsigned long r3=0;
	unsigned char j;
	
	for (j=0;j<10;j++)
	{
		SPI_write(0xff);
	}
	
	SDC_sendcommand(READ_OCR,0,0);
	r1=SPI_read();
	
	if (r1==1)
	{
		for (j=0;j<4;j++)
		{
			r3<<=8;
			r3|=SPI_read();
		}
		return r3;
	}
	return SDC_ERROR;
}

void CMD55()
{
	SDC_sendcommand(APP_CMD,0,0);
}

void ACMD41()
{
	unsigned char r1;
	do 
	{
		CMD55();
		SDC_sendcommand(SD_SEND_OP_COND,0,0);
		r1=SPI_read();
	} while (r1==1);
}

unsigned char SDC_init()
{	
	unsigned char SDC_version=SDC_V1;
	
	DDRB|=1<<SDC_CS;
	DDRB|=1<<SDC_SS;
	DDRB|=1<<SDC_MOSI;
	DDRB&=~(1<<SDC_MISO);
	DDRB|=1<<SDC_SCK;
	
	SPI_init_lospeed();	
	
	SDC_enable();
	
	if(CMD0()==SDC_ERROR)
	{
		return SDC_ERROR;
	}
	SDC_version=CMD8();
	if(SDC_version==SDC_ERROR)
	{
		return SDC_ERROR;
	}
	ACMD41();
	if (SDC_version==SDC_V1)
	{
		CMD16();
	}
	
	SDC_disable();
	
	SPI_init_hispeed();
	
	return 0;
}

unsigned char SDC_readblock(unsigned long addr,unsigned char buff[],unsigned short *crc16)
{
	unsigned short i;
	unsigned char r1;
	
	SDC_enable();		
	
	for (i=0;i<10;i++)
	{
		SPI_write(0xff);
	}
		
	SDC_sendcommand(READ_SINGLE_BLOCK,addr,0);
	r1=SPI_read();
	
	if (r1==1)
	{
		i=0;
		while (SPI_read()!=0xfe)
		{
			i++;
			if (i>200)
			{
				return SDC_ERROR;
			}
		}
		
		for (i=0;i<512;i++)
		{
			buff[i]=SPI_read();
		}
		
		*crc16|=SPI_read();
		*crc16<<=8;
		*crc16|=SPI_read();
		
		return SDC_READ_FINISH;
	}
	else
	{
		return SDC_ERROR;
	}
	
	SDC_disable();
	return r1;
}

unsigned char SDC_writeblock(unsigned long addr,unsigned char buff[],unsigned short crc16)
{
	unsigned short i;
	unsigned char r1;
	
	SDC_enable();		
	
	for (i=0;i<10;i++)
	{
		SPI_write(0xff);
	}
	
	SDC_sendcommand(WRITE_BLOCK,addr,0);
	r1=SPI_read();
	
	if (r1==1)
	{
		SPI_write(0xff);
		SPI_write(0xff);
		
		SPI_write(0xfe);
		for (i=0;i<512;i++)
		{
			SPI_write(buff[i]);
		}
		
		SPI_write(crc16>>8);
		SPI_write(crc16);
	}
	
	for (i=0;i<200;i++)
	{
		r1=SPI_read();
		r1&=0xf;

		if (r1==SDC_WRITE_OK)
		{
			break;
		} 
		else if (r1==SDC_WRITE_CRCERROR)
		{
			break;
		}
		else if (r1==SDC_WRITE_ERROR)
		{
			break;
		}
	}
	
	if (i==200)
	{
		return SDC_ERROR;
	}
	
	while(SPI_read()==0);
	
	SDC_disable();
	return r1;
}





